package blackstar.performance;

import java.text.NumberFormat;
import java.util.logging.Logger;

import blackstar.performance.logger.RollingFileLogger;
import blackstar.performance.logger.info.LoadLogger;

/**
 * @author RayWoo (ayufox@gmail.com)
 * 
 */
public class InvokeDetailPerformance extends PerformanceStat implements
		InvokeDetailPerformanceMBean
{
	private final static Logger LOGGER = Logger
			.getLogger(InvokePerformance.class.getSimpleName());

	public final static String BEAN_NAME = "name=Detail,type=InvokePerformance";

	public final static String CLASS_NAME = InvokeDetailPerformance.class
			.getName().replace('.', '/');
	public final static String ENTRY_METHOD = "entryMethod";
	public final static String EXIT_METHOD = "exitMethod";
	public final static String METHOD_SYNTAX = "(Ljava/lang/String;Ljava/lang/String;)V";

	private final static boolean stat;
	static
	{
		String isStat = System.getProperty("statperm");
		stat = !("false".equalsIgnoreCase(isStat));
		LOGGER.info("state:" + stat);
	}
	private final static Object ENTRY_LOCK = new Object();
	private static long totalEntryInvokes = 0L;
	private static long totalEntryPerfTimeNano = 0L;
	private final static Object EXIT_LOCK = new Object();
	private static long totalExitInvokes = 0L;
	private static long totalExitPerfTimeNano = 0L;

	public static boolean isStat()
	{
		return InvokeDetailPerformance.stat;
	}

	public static long getEntryTotalInvokes()
	{
		return InvokeDetailPerformance.totalEntryInvokes;
	}

	public static long getEntryTotalPerfTimeNano()
	{
		return InvokeDetailPerformance.totalEntryPerfTimeNano;
	}

	public static long getExitTotalInvokes()
	{
		return InvokeDetailPerformance.totalExitInvokes;
	}

	public static long getExitTotalPerfTimeNano()
	{
		return InvokeDetailPerformance.totalExitPerfTimeNano;
	}

	private static LoadLogger loadLogger;

	public static void init(RollingFileLogger rollingFileLogger,
			long bufferSize, boolean appendTime)
	{
		InvokeDetailPerformance.loadLogger = new LoadLogger(rollingFileLogger,
				appendTime);
		if (InvokeDetailPerformance.isStat())
		{
			InvokeDetailPerformance invokeDetailPerformance = new InvokeDetailPerformance();
			invokeDetailPerformance.setBufferSize(bufferSize);
			rollingFileLogger.setLogStat(invokeDetailPerformance);
			MBeanRegister.registMBean(BEAN_NAME, invokeDetailPerformance);
		}
	}

	public static void entryMethod(String className, String methodName)
	{
		if (InvokeDetailPerformance.stat)
		{
			long time = System.nanoTime();
			loadLogger.start(className, methodName);
			time = System.nanoTime() - time;
			synchronized (ENTRY_LOCK)
			{
				if (time > 0)
				{
					totalEntryPerfTimeNano += time;
				}
				totalEntryInvokes++;
			}
		} else
		{
			loadLogger.start(className, methodName);
		}
	}

	public static void exitMethod(String className, String methodName)
	{
		if (stat)
		{
			long time = System.nanoTime();
			loadLogger.stop();
			time = System.nanoTime() - time;
			synchronized (EXIT_LOCK)
			{
				if (time > 0)
				{
					totalExitPerfTimeNano += time;
				}
				totalExitInvokes++;
			}
		} else
		{
			loadLogger.stop();
		}
	}

	public long getEntryInvokes()
	{
		return InvokeDetailPerformance.totalEntryInvokes;
	}

	public long getEntryPerfTimeNano()
	{
		return InvokeDetailPerformance.totalEntryPerfTimeNano;
	}

	public long getExitInvokes()
	{
		return InvokeDetailPerformance.totalExitInvokes;
	}

	public long getExitPerfTimeNano()
	{
		return InvokeDetailPerformance.totalExitPerfTimeNano;
	}

	private final static long MILLISECONDS = 1000000L;

	public String getInvokeMessage()
	{
		long entryInvokes = InvokeDetailPerformance.totalEntryInvokes;
		long entryTimePerfNano = InvokeDetailPerformance.totalEntryPerfTimeNano;
		long exitInvokes = InvokeDetailPerformance.totalExitInvokes;
		long exitTimePerfNano = InvokeDetailPerformance.totalExitPerfTimeNano;

		NumberFormat timeFormat = NumberFormat.getNumberInstance();
		timeFormat.setMaximumFractionDigits(3);

		StringBuilder message = new StringBuilder();
		message.append("Invokes=");
		message.append(entryInvokes);
		message.append(", PerfTimes=");
		message.append(timeFormat
				.format(((double) (entryTimePerfNano + exitTimePerfNano))
						/ MILLISECONDS));
		message.append("ms");
		message.append(", AvarageTimes=");
		if (entryInvokes == 0)
		{
			message.append("?");
		} else
		{
			message.append(timeFormat
					.format(((double) (entryTimePerfNano + exitTimePerfNano))
							/ entryInvokes));
		}
		message.append("ns");
		message.append(", EntryInvokes=");
		message.append(entryInvokes);
		message.append(", EntryPerfTimes=");
		message.append(timeFormat.format(((double) entryTimePerfNano)
				/ MILLISECONDS));
		message.append("ms");
		message.append(", EntryAvarageTimes=");
		if (entryInvokes == 0)
		{
			message.append("?");
		} else
		{
			message.append(timeFormat.format(((double) entryTimePerfNano)
					/ entryInvokes));
		}
		message.append("ns");
		message.append(", ExitInvokes=");
		message.append(exitInvokes);
		message.append(", ExitPerfTimes=");
		message.append(timeFormat.format(((double) exitTimePerfNano)
				/ MILLISECONDS));
		message.append("ms");
		message.append(", ExitAvarageTimes=");
		if (exitInvokes == 0)
		{
			message.append("?");
		} else
		{
			message.append(timeFormat.format(((double) exitTimePerfNano)
					/ exitInvokes));
		}
		message.append("ns");

		return message.toString();
	}
}